#include <sys/ptrace.h>
#include <linux/ptrace.h>
#include <linux/seccomp.h>
#include <seccomp.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <sys/syscall.h>
#include <stdio.h>
#include <unistd.h>
#include <memory.h>
const char *const sysCallList[] =
{
"__NR_read","__NR_write","__NR_open","__NR_close","__NR_stat","__NR_fstat","__NR_lstat","__NR_poll","__NR_lseek","__NR_mmap","__NR_mprotect","__NR_munmap","__NR_brk","__NR_rt_sigaction","__NR_rt_sigprocmask","__NR_rt_sigreturn","__NR_ioctl","__NR_pread64","__NR_pwrite64","__NR_readv","__NR_writev","__NR_access","__NR_pipe","__NR_select","__NR_sched_yield","__NR_mremap","__NR_msync","__NR_mincore","__NR_madvise","__NR_shmget","__NR_shmat","__NR_shmctl","__NR_dup","__NR_dup2","__NR_pause","__NR_nanosleep","__NR_getitimer","__NR_alarm","__NR_setitimer","__NR_getpid","__NR_sendfile","__NR_socket","__NR_connect","__NR_accept","__NR_sendto","__NR_recvfrom","__NR_sendmsg","__NR_recvmsg","__NR_shutdown","__NR_bind","__NR_listen","__NR_getsockname","__NR_getpeername","__NR_socketpair","__NR_setsockopt","__NR_getsockopt","__NR_clone","__NR_fork","__NR_vfork","__NR_execve","__NR_exit","__NR_wait4","__NR_kill","__NR_uname","__NR_semget","__NR_semop","__NR_semctl","__NR_shmdt","__NR_msgget","__NR_msgsnd","__NR_msgrcv","__NR_msgctl","__NR_fcntl","__NR_flock","__NR_fsync","__NR_fdatasync","__NR_truncate","__NR_ftruncate","__NR_getdents","__NR_getcwd","__NR_chdir","__NR_fchdir","__NR_rename","__NR_mkdir","__NR_rmdir","__NR_creat","__NR_link","__NR_unlink","__NR_symlink","__NR_readlink","__NR_chmod","__NR_fchmod","__NR_chown","__NR_fchown","__NR_lchown","__NR_umask","__NR_gettimeofday","__NR_getrlimit","__NR_getrusage","__NR_sysinfo","__NR_times","__NR_ptrace","__NR_getuid","__NR_syslog","__NR_getgid","__NR_setuid","__NR_setgid","__NR_geteuid","__NR_getegid","__NR_setpgid","__NR_getppid","__NR_getpgrp","__NR_setsid","__NR_setreuid","__NR_setregid","__NR_getgroups","__NR_setgroups","__NR_setresuid","__NR_getresuid","__NR_setresgid","__NR_getresgid","__NR_getpgid","__NR_setfsuid","__NR_setfsgid","__NR_getsid","__NR_capget","__NR_capset","__NR_rt_sigpending","__NR_rt_sigtimedwait","__NR_rt_sigqueueinfo","__NR_rt_sigsuspend","__NR_sigaltstack","__NR_utime","__NR_mknod","__NR_uselib","__NR_personality","__NR_ustat","__NR_statfs","__NR_fstatfs","__NR_sysfs","__NR_getpriority","__NR_setpriority","__NR_sched_setparam","__NR_sched_getparam","__NR_sched_setscheduler","__NR_sched_getscheduler","__NR_sched_get_priority_max","__NR_sched_get_priority_min","__NR_sched_rr_get_interval","__NR_mlock","__NR_munlock","__NR_mlockall","__NR_munlockall","__NR_vhangup","__NR_modify_ldt","__NR_pivot_root","__NR__sysctl","__NR_prctl","__NR_arch_prctl","__NR_adjtimex","__NR_setrlimit","__NR_chroot","__NR_sync","__NR_acct","__NR_settimeofday","__NR_mount","__NR_umount2","__NR_swapon","__NR_swapoff","__NR_reboot","__NR_sethostname","__NR_setdomainname","__NR_iopl","__NR_ioperm","__NR_create_module","__NR_init_module","__NR_delete_module","__NR_get_kernel_syms","__NR_query_module","__NR_quotactl","__NR_nfsservctl","__NR_getpmsg","__NR_putpmsg","__NR_afs_syscall","__NR_tuxcall","__NR_security","__NR_gettid","__NR_readahead","__NR_setxattr","__NR_lsetxattr","__NR_fsetxattr","__NR_getxattr","__NR_lgetxattr","__NR_fgetxattr","__NR_listxattr","__NR_llistxattr","__NR_flistxattr","__NR_removexattr","__NR_lremovexattr","__NR_fremovexattr","__NR_tkill","__NR_time","__NR_futex","__NR_sched_setaffinity","__NR_sched_getaffinity","__NR_set_thread_area","__NR_io_setup","__NR_io_destroy","__NR_io_getevents","__NR_io_submit","__NR_io_cancel","__NR_get_thread_area","__NR_lookup_dcookie","__NR_epoll_create","__NR_epoll_ctl_old","__NR_epoll_wait_old","__NR_remap_file_pages","__NR_getdents64","__NR_set_tid_address","__NR_restart_syscall","__NR_semtimedop","__NR_fadvise64","__NR_timer_create","__NR_timer_settime","__NR_timer_gettime","__NR_timer_getoverrun","__NR_timer_delete","__NR_clock_settime","__NR_clock_gettime","__NR_clock_getres","__NR_clock_nanosleep","__NR_exit_group","__NR_epoll_wait","__NR_epoll_ctl","__NR_tgkill","__NR_utimes","__NR_vserver","__NR_mbind","__NR_set_mempolicy","__NR_get_mempolicy","__NR_mq_open","__NR_mq_unlink","__NR_mq_timedsend","__NR_mq_timedreceive","__NR_mq_notify","__NR_mq_getsetattr","__NR_kexec_load","__NR_waitid","__NR_add_key","__NR_request_key","__NR_keyctl","__NR_ioprio_set","__NR_ioprio_get","__NR_inotify_init","__NR_inotify_add_watch","__NR_inotify_rm_watch","__NR_migrate_pages","__NR_openat","__NR_mkdirat","__NR_mknodat","__NR_fchownat","__NR_futimesat","__NR_newfstatat","__NR_unlinkat","__NR_renameat","__NR_linkat","__NR_symlinkat","__NR_readlinkat","__NR_fchmodat","__NR_faccessat","__NR_pselect6","__NR_ppoll","__NR_unshare","__NR_set_robust_list","__NR_get_robust_list","__NR_splice","__NR_tee","__NR_sync_file_range","__NR_vmsplice","__NR_move_pages","__NR_utimensat","__NR_epoll_pwait","__NR_signalfd","__NR_timerfd_create","__NR_eventfd","__NR_fallocate","__NR_timerfd_settime","__NR_timerfd_gettime","__NR_accept4","__NR_signalfd4","__NR_eventfd2","__NR_epoll_create1","__NR_dup3","__NR_pipe2","__NR_inotify_init1","__NR_preadv","__NR_pwritev","__NR_rt_tgsigqueueinfo","__NR_perf_event_open","__NR_recvmmsg","__NR_fanotify_init","__NR_fanotify_mark","__NR_prlimit64","__NR_name_to_handle_at","__NR_open_by_handle_at","__NR_clock_adjtime","__NR_syncfs","__NR_sendmmsg","__NR_setns","__NR_getcpu","__NR_process_vm_readv","__NR_process_vm_writev","__NR_kcmp","__NR_finit_module","__NR_sched_setattr","__NR_sched_getattr","__NR_renameat2","__NR_seccomp","__NR_getrandom","__NR_memfd_create","__NR_kexec_file_load","__NR_bpf","__NR_execveat","__NR_userfaultfd","__NR_membarrier","__NR_mlock2","__NR_copy_file_range","__NR_preadv2","__NR_pwritev2","__NR_pkey_mprotect","__NR_pkey_alloc","__NR_pkey_free","__NR_statx"
};
int main(){
    pid_t child;
    long orig_rax;
    int status;
    int iscalling=0;
    struct user_regs_struct regs;
    struct ptrace_syscall_info syscall_info;
    child = vfork();
    int forbidden_syscalls[] = { __NR_fork, __NR_vfork};
    int forbidden_syscalls_size = sizeof(forbidden_syscalls) / sizeof(int);
    if(child == 0){

        scmp_filter_ctx ctx;
        ctx = seccomp_init(SCMP_ACT_ALLOW);

        printf("Adding rule : Deny read to any FD greater than 0 \n");
        seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(read), 2,
        SCMP_A0(SCMP_CMP_NE, 0), SCMP_A0(SCMP_CMP_NE, 3));

        printf("Adding rule : Deny write to any FD except 1, 2 \n");
        seccomp_rule_add(ctx, SCMP_ACT_TRACE(__NR_write), __NR_write, 2,SCMP_A0(SCMP_CMP_GT, 0), SCMP_A0(SCMP_CMP_LT, 2));
        seccomp_rule_add(ctx, SCMP_ACT_TRACE(__NR_write), __NR_write,0);

        seccomp_rule_add(ctx, SCMP_ACT_TRACE(__NR_fork), __NR_fork,0);
        seccomp_rule_add(ctx, SCMP_ACT_TRACE(__NR_vfork), __NR_vfork,0);
        seccomp_rule_add(ctx, SCMP_ACT_TRACE(__NR_clone), __NR_clone,0);

        seccomp_load(ctx);
        seccomp_release(ctx);

        // 在下一次执行exec或exit时阻塞自身并通知跟踪进程 
        ptrace(PTRACE_TRACEME,0,NULL,NULL);

        execl("./a.out", "./a.out", NULL);

    }else{
        while(1){
            waitpid(child,&status,0);
            puts("================");
            printf("status:%d\n", status);
            if (status>>8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP<<8))) {
                puts("recieve event");
                unsigned long event_id = 0;
                ptrace(PTRACE_GETEVENTMSG,child,NULL,&event_id);
                printf("invalid syscall seccomp event:%d,syscall[%s]\n", event_id, sysCallList[event_id]);
                goto cont_monitor;
                // kill(child, SIGKILL);
                // break;
            }
            if(WIFEXITED(status)) {
                puts("child process exit");
                break;
            }
            if (WIFSIGNALED(status)) {
                puts("recieve singal");
                int sig = WTERMSIG(status);
                printf("sig:%d\n", sig);
                if (sig == SIGKILL) {
                    puts("kill");
                    kill(child, SIGKILL);
                    break;
                } else if (sig == SIGSYS) {
                    puts("invalid syscall");
                    kill(child, SIGKILL);
                    break;
                }
            }
            if (WIFSTOPPED(status)) {
                puts("stopped");
                int sig = WSTOPSIG(status);
                printf("sig:%d\n", sig);
                if (sig == SIGSTOP) {
                    puts("stopped child");
                    goto cont_monitor;
                }
            }
            orig_rax = ptrace(PTRACE_PEEKUSER,child,8*ORIG_RAX,NULL);
            if (orig_rax == -1) {
                puts("orig_rax == -1, error");
                break;
            }
            ptrace(PTRACE_GETREGS,child,NULL,&regs);
            printf("syscall %ld[%s], %lld %lld %lld\n",orig_rax, sysCallList[orig_rax], regs.rdi, regs.rsi, regs.rdx);
cont_monitor:
            ptrace(PTRACE_SETOPTIONS,child,NULL,PTRACE_O_TRACESECCOMP);
            ptrace(PTRACE_CONT,child,NULL,NULL);
        }
   }
}